/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.florent37.camerafragment.internal.manager.impl;

import ohos.aafwk.ability.fraction.FractionAbility;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.Surface;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.utils.Point;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.media.camera.CameraKit;
import ohos.media.camera.device.Camera;
import ohos.media.camera.device.CameraAbility;
import ohos.media.camera.device.CameraConfig;
import ohos.media.camera.device.CameraInfo;
import ohos.media.camera.device.CameraStateCallback;
import ohos.media.camera.device.FrameConfig;
import ohos.media.camera.device.FrameResult;
import ohos.media.camera.device.FrameStateCallback;
import ohos.media.camera.params.AfResult;
import ohos.media.camera.params.Metadata;
import ohos.media.camera.params.ParameterKey;
import ohos.media.common.AudioProperty;
import ohos.media.common.Source;
import ohos.media.common.StorageProperty;
import ohos.media.common.VideoProperty;
import ohos.media.image.Image;
import ohos.media.image.ImageReceiver;
import ohos.media.image.common.ImageFormat;
import ohos.media.recorder.Recorder;
import ohos.media.recorder.RecorderProfile;

import com.github.florent37.camerafragment.configuration.Configuration;
import com.github.florent37.camerafragment.configuration.ConfigurationProvider;
import com.github.florent37.camerafragment.internal.manager.listener.CameraCloseListener;
import com.github.florent37.camerafragment.internal.manager.listener.CameraOpenListener;
import com.github.florent37.camerafragment.internal.manager.listener.CameraPhotoListener;
import com.github.florent37.camerafragment.internal.manager.listener.CameraVideoListener;
import com.github.florent37.camerafragment.internal.ui.model.PhotoQualityOption;
import com.github.florent37.camerafragment.internal.ui.model.VideoQualityOption;
import com.github.florent37.camerafragment.internal.utils.CameraHelper;
import com.github.florent37.camerafragment.internal.utils.ImageSaver;
import com.github.florent37.camerafragment.internal.utils.Log;
import com.github.florent37.camerafragment.internal.utils.Size;
import com.github.florent37.camerafragment.listeners.CameraFragmentResultListener;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PICTURE;
import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PREVIEW;
import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_RECORD;

/**
 * 相机管理1
 *
 * @since 2021-04-09
 */
public class Camera1Manager extends BaseCameraManager<Integer, SurfaceOps.Callback> {
    private static final String TAG = "Camera1Manager";
    private static final int ORIENTATION_NORMAL = 1;
    private static final int ORIENTATION_ROTATE_90 = 6;
    private static final int ORIENTATION_ROTATE_180 = 3;
    private static final int ORIENTATION_ROTATE_270 = 8;
    private static final int AUDIO_NUM_CHANNELS_STEREO = 2;
    private static final int AUDIO_SAMPLE_RATE_HZ = 8000;
    private static final int BIT_RATE = 10000000;
    private static final int FPS = 30;
    private static final int CONSTANT_0 = 0;
    private static final int CONSTANT_1 = 1;
    private static final int CONSTANT_2 = 2;
    private static final int CONSTANT_3 = 3;
    private static final int DEGREE_0 = 0;
    private static final int DEGREE_180 = 180;
    private static final int ROTATE_90 = 90;
    private static final int ROTATE_180 = 180;
    private static final int ROTATE_270 = 270;
    private static final int ROTATE_360 = 360;
    private static final double CONSTANT_0_6 = 0.6;
    private static final double CONSTANT_0_7 = 0.7;
    private static final double CONSTANT_0_9 = 0.9;
    private static final double CONSTANT_1_1 = 1.1;
    private static final double CONSTANT_1_2 = 1.2;
    private static final double CONSTANT_1_3 = 1.3;
    private static final double CONSTANT_1_35 = 1.35;
    private static final double CONSTANT_1_5 = 1.5;
    private static final double CONSTANT_1_6 = 1.6;
    private static final double CONSTANT_3_4 = 3.4;
    private static final double CONSTANT_3_5 = 3.5;
    private static final double CONSTANT_6 = 6;
    private static final double CONSTANT_8 = 8;
    private static final double CONSTANT_10 = 10;
    private static final int CONSTANT_5 = 5;
    private static final int CONSTANT_600 = 600;

    private Camera cameraCallback;
    private CameraKit cameraKit;
    private Surface surface;

    private int orientation;
    private int displayRotation = 0;
    private int screenRotation;

    private File outputPath;
    private String cameraIdString;
    private int futurFlashMode;
    private CameraVideoListener videoListener;
    private CameraPhotoListener photoListener;
    private CameraFragmentResultListener callback;
    private CameraStateCallbackImpl cameraStateCallback;
    private CameraStateCallbackImpl cameraStateCallback2;
    private ImageReceiver imageReceiver;
    private FrameConfig previewFrameConfig;
    private FrameConfig previewRecordConfig;
    private FrameConfig framePictureConfig;
    private FrameConfig.Builder frameConfigBuilder;
    private FrameConfig.Builder frameRecordConfigBuilder;
    private FrameConfig.Builder framePictureConfigBuilder;
    private Source source; // 音视频源
    private AudioProperty.Builder audioPropertyBuilder; // 音频属性构造器
    private VideoProperty.Builder videoPropertyBuilder; // 视频属性构造器
    private StorageProperty.Builder storagePropertyBuilder; // 音视频存储属性构造器
    private SurfaceProvider surfaceProvider;
    private DirectionalLayout dependentLayout;
    private FractionAbility fractionAbility;
    private int mediaAction;
    private List<Size> photoSizeList;
    private List<Size> videoSizeList;
    private double tempWidth;
    private double tempHeight;

    /**
     * 打开相机
     *
     * @param context 上下文
     * @param camera 相机id
     * @param listener 打开相机接口
     */
    @Override
    public void openCamera(Context context, int camera, CameraOpenListener<Integer, SurfaceOps.Callback> listener) {
        try {
            this.mCurrentCameraId = camera;
            prepareCameraOutputs(String.valueOf(camera));
            takePictureInit(); // 准备好预览画面大小为拍照准备
            if (futurFlashMode != 0) {
                setFlashMode(futurFlashMode);
                futurFlashMode = 0;
            }
            cameraKit.createCamera(String.valueOf(camera), cameraStateCallback, uiHandler); // 创建相机
            setSurfaceSize();
            if (listener != null) {
                listener.onCameraOpened(camera, previewSize, new OpenCameraSurfaceCallback());
            }
        } catch (IllegalStateException error) {
            Log.debug(TAG, "Can't open camera: " + error.getMessage());
            if (listener != null) {
                listener.onCameraOpenError();
            }
        }
    }

    private void setSurfaceSize() {
        if (dependentLayout != null) {
            Size compare = compare(previewSize.getWidth(), previewSize.getHeight());
            DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig();
            params.width = compare.getWidth();
            params.height = compare.getHeight();
            surfaceProvider.setLayoutConfig(params);
            surfaceProvider.postLayout();
        }
    }

    /**
     * 关闭相机
     *
     * @param cameraCloseListener 关闭相机监听
     */
    @Override
    public void closeCamera(CameraCloseListener cameraCloseListener) {
        releaseCamera();
        if (cameraCloseListener != null) {
            cameraCloseListener.onCameraClosed(mCurrentCameraId);
        }
    }

    /**
     * 为拍照初始化做准备
     *
     * @param camera 相机Id
     */
    @Override
    protected void prepareCameraOutputs(String camera) {
        if (configurationProvider.getMediaPhotoQuality() == Configuration.MEDIA_QUALITY_AUTO) {
            long videoFileSize = configurationProvider.getVideoFileSize();
            int minimumVideoDuration = configurationProvider.getMinimumVideoDuration();
            camcorderProfile = CameraHelper.getCamcorderProfile(mCurrentCameraId, videoFileSize, minimumVideoDuration);
        } else {
            if (mediaAction == Configuration.MEDIA_ACTION_PHOTO) {
                int mediaPhotoQuality = configurationProvider.getMediaPhotoQuality();
                camcorderProfile = CameraHelper.getCamcorderProfile(mediaPhotoQuality, camera);
            } else {
                int mediaVideoQuality = configurationProvider.getMediaVideoQuality();
                camcorderProfile = CameraHelper.getCamcorderProfile(mediaVideoQuality, camera);
            }
        }
        CameraAbility cameraAbility = cameraKit.getCameraAbility(camera);
        List<ohos.media.image.common.Size> supportedSizes = cameraAbility.getSupportedSizes(ImageFormat.JPEG);
        List<Size> previewSizes = Size.fromList2(supportedSizes);
        List<Size> pictureList = Size.fromList2(supportedSizes);
        List<Size> videoSizes = Size.fromList2(supportedSizes);

        // 重新获取分辨率
        getVideoQualityOptions();
        getPhotoQualityOptions();

        videoSize = CameraHelper.getSizeWithClosestRatio(
            (videoSizes == null || videoSizes.isEmpty()) ? previewSizes : videoSizeList,
            camcorderProfile.vFrameWidth, camcorderProfile.vFrameHeight);

        if (configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_PHOTO
            || configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_UNSPECIFIED) {
            photoSize = CameraHelper.getPictureSize2((pictureList == null || pictureList.isEmpty())
                ? previewSizes : photoSizeList, configurationProvider.getMediaPhotoQuality());
        } else {
            photoSize = CameraHelper.getPictureSize2((pictureList == null || pictureList.isEmpty())
                ? previewSizes : photoSizeList, configurationProvider.getMediaVideoQuality());
        }

        if (configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_PHOTO
            || configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_UNSPECIFIED) {
            previewSize = CameraHelper.getSizeWithClosestRatio(previewSizes,
                photoSize.getWidth(), photoSize.getHeight());
        } else {
            previewSize = CameraHelper.getSizeWithClosestRatio(previewSizes,
                videoSize.getWidth(), videoSize.getHeight());
        }
    }

    @Override
    public void prepareVideoRecorder() { // 录制视频
        context = context == null ? fractionAbility : context;
        source = new Source();
        videoRecorder = new Recorder();
        videoPropertyBuilder = new VideoProperty.Builder();
        audioPropertyBuilder = new AudioProperty.Builder();
        storagePropertyBuilder = new StorageProperty.Builder();
        videoPropertyBuilder.setRecorderBitRate(BIT_RATE); // 设置录制比特率
        if (mCurrentCameraId.equals(faceFrontCameraId)) { // 前置摄像头
            if (screenRotation == 0) { // 只判断竖屏
                videoPropertyBuilder.setRecorderDegrees(getOrientation(CONSTANT_3)); // 设置录像方向
            }
        } else { // 后置摄像头
            if (screenRotation == DEGREE_0 || screenRotation == DEGREE_180) { // 判断旋转角度是否为0 或180
                videoPropertyBuilder.setRecorderDegrees(getOrientation(CONSTANT_1)); // 设置录像方向为旋转0
            } else {
                videoPropertyBuilder.setRecorderDegrees(getOrientation(CONSTANT_0)); // 设置录像方向为旋转0
            }
        }
        videoPropertyBuilder.setRecorderFps(FPS); // 设置录制采样率
        videoPropertyBuilder.setRecorderRate(FPS); // 设置录制帧率
        videoPropertyBuilder.setRecorderHeight(Math.min(previewSize.getHeight(),
            previewSize.getWidth())); // 设置录像支持的分辨率，需保证width > height
        videoPropertyBuilder.setRecorderWidth(Math.max(previewSize.getHeight(),
            previewSize.getWidth()));
        videoPropertyBuilder.setRecorderVideoEncoder(Recorder.VideoEncoder.H264); // 设置视频编码方式
        source.setRecorderAudioSource(Recorder.AudioSource.MIC); // 设置录制音频源
        source.setRecorderVideoSource(Recorder.VideoSource.SURFACE); // 设置视频窗口
        videoRecorder.setSource(source); // 设置音视频源
        videoRecorder.setOutputFormat(Recorder.OutputFormat.MPEG_4); // 设置音视频输出格式

        StringBuffer fileName = new StringBuffer("VID_"); // 生成随机文件名
        fileName.append(UUID.randomUUID()).append(".mp4");
        File file = new File(context.getExternalFilesDir("").getAbsolutePath(), fileName.toString()); // 创建录像文件对象
        storagePropertyBuilder.setRecorderFile(file); // 设置存储音视频文件名
        audioPropertyBuilder.setRecorderAudioEncoder(Recorder.AudioEncoder.AAC); // 设置音频编码格式
        audioPropertyBuilder.setRecorderNumChannels(AUDIO_NUM_CHANNELS_STEREO);
        audioPropertyBuilder.setRecorderSamplingRate(AUDIO_SAMPLE_RATE_HZ);
        videoRecorder.setStorageProperty(storagePropertyBuilder.build()); // 设置存储属性
        videoRecorder.setAudioProperty(audioPropertyBuilder.build()); // 设置音频属性
        videoRecorder.setVideoProperty(videoPropertyBuilder.build()); // 设置视频属性
        videoRecorder.prepare();
    }

    @Override
    public void setScreenRotation(int degrees) {
        screenRotation = degrees;
    }

    @Override
    public void releaseCameraResource() {
        releaseCamera();
    }

    // 设置录制方向
    private int getOrientation(int rotation) {
        int ori = CONSTANT_0;
        switch (rotation) {
            case CONSTANT_0: // 默认竖屏
                orientation = Recorder.OrientationHint.FIRST_PLAYBACK_DERGREE;
                ori = Recorder.OrientationHint.FIRST_PLAYBACK_DERGREE;
                break;
            case CONSTANT_1: // 旋转90
                orientation = Recorder.OrientationHint.SECOND_PLAYBACK_DERGREE;
                ori = Recorder.OrientationHint.SECOND_PLAYBACK_DERGREE;
                break;
            case CONSTANT_2: // 旋转180
                orientation = Recorder.OrientationHint.THIRD_PLAYBACK_DERGREE;
                ori = Recorder.OrientationHint.THIRD_PLAYBACK_DERGREE;
                break;
            case CONSTANT_3: // 旋转270
                orientation = Recorder.OrientationHint.FOURTH_PLAYBACK_DERGREE;
                ori = Recorder.OrientationHint.FOURTH_PLAYBACK_DERGREE;
                break;
            default:
                break;
        }

        return ori;
    }

    /**
     * 最大持续时间
     */
    @Override
    protected void onMaxDurationReached() {
        stopVideoRecord(null);
    }

    @Override
    protected void onMaxFileSizeReached() {
        stopVideoRecord(null);
    }

    @Override
    protected int getPhotoOrientation(int sensorPosition) {
        final int rotate;
        if (mCurrentCameraId.equals(faceFrontCameraId)) {
            rotate = (ROTATE_360 + faceFrontCameraOrientation + configurationProvider.getDegrees()) % ROTATE_360;
        } else {
            rotate = (ROTATE_360 + faceBackCameraOrientation - configurationProvider.getDegrees()) % ROTATE_360;
        }
        if (rotate == 0) {
            orientation = ORIENTATION_NORMAL;
        } else if (rotate == ROTATE_90) {
            orientation = ORIENTATION_ROTATE_90;
        } else if (rotate == ROTATE_180) {
            orientation = ORIENTATION_ROTATE_180;
        } else if (rotate == ROTATE_270) {
            orientation = ORIENTATION_ROTATE_270;
        } else {
            orientation = ORIENTATION_NORMAL;
        }
        return orientation;
    }

    @Override
    protected int getVideoOrientation(int sensorPosition) {
        int degrees = 0;
        switch (sensorPosition) {
            case Configuration.SENSOR_POSITION_UP:
                degrees = 0;
                break; // Natural orientation
            case Configuration.SENSOR_POSITION_LEFT:
                degrees = ROTATE_90;
                break; // Landscape left
            case Configuration.SENSOR_POSITION_UP_SIDE_DOWN:
                degrees = ROTATE_180;
                break; // Upside down
            case Configuration.SENSOR_POSITION_RIGHT:
                degrees = ROTATE_270;
                break; // Landscape right
            default:
                break;
        }

        final int rotate;
        if (mCurrentCameraId.equals(faceFrontCameraId)) {
            rotate = (ROTATE_360 + faceFrontCameraOrientation + degrees) % ROTATE_360;
        } else {
            rotate = (ROTATE_360 + faceBackCameraOrientation - degrees) % ROTATE_360;
        }
        return rotate;
    }

    /**
     * 设置闪光灯模式
     *
     * @param flashMode 闪光灯类型
     */
    @Override
    public void setFlashMode(int flashMode) {
        if (cameraCallback != null) {
            setFlashMode(cameraCallback, flashMode);
        } else {
            futurFlashMode = flashMode;
        }
    }

    private void setFlashMode(Camera camera, int flashMode) {
        if (frameConfigBuilder == null) {
            frameConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_PICTURE);
        }
        switch (flashMode) {
            case Configuration.FLASH_MODE_ON:
                frameConfigBuilder.setFlashMode(Metadata.FlashMode.FLASH_ALWAYS_OPEN);
                break;
            case Configuration.FLASH_MODE_OFF:
                frameConfigBuilder.setFlashMode(Metadata.FlashMode.FLASH_CLOSE);
                break;
            case Configuration.FLASH_MODE_AUTO:
            default:
                frameConfigBuilder.setFlashMode(Metadata.FlashMode.FLASH_AUTO);
                break;
        }
    }

    @Override
    public void takePhoto(File photoFile, CameraPhotoListener listener, CameraFragmentResultListener resultListener) {
        this.outputPath = photoFile;
        this.photoListener = listener;
        this.callback = resultListener;
        backgroundHandler.postTask(new Runnable() {
            @Override
            public void run() {
                capture(); // 开始拍照拍照回调在 imageArrivalListener
            }
        });
    }

    private void setAutoFocus(CameraAbility cameraAbility, FrameConfig.Builder parameters) { // 设置自动对焦
        for (int index : cameraAbility.getSupportedAfMode()) {
            if (cameraAbility.getSupportedAfMode()[index] == AfResult.State.AF_STATE_AUTO_FOCUSED) {
                parameters.setAfMode(AfResult.State.AF_STATE_AUTO_FOCUSED, null);
                break;
            }
        }
    }

    // 录制视频
    @Override
    public void startVideoRecord(File videoFile, CameraVideoListener cameraVideoListener) {
        if (isVideoRecording) {
            return;
        }
        this.outputPath = videoFile;
        this.videoListener = cameraVideoListener;
        if (videoListener != null) { // 开启录像时要重新获取视频预览页面的款到
            prepareCameraOutputs(mCurrentCameraId.toString());
            prepareVideoRecorder();
            cameraKit.createCamera(mCurrentCameraId.toString(), cameraStateCallback2, uiHandler);
        }
    }

    /**
     * 获取图片质量大小
     *
     * @param mediaQuality 质量类型
     * @return Size 图片大小
     */
    @Override
    public Size getPhotoSizeForQuality(int mediaQuality) {
        CameraAbility cameraAbility = cameraKit.getCameraAbility(mCurrentCameraId.toString());
        if (cameraAbility != null) {
            return CameraHelper.getPictureSize(
                Size.fromList2(cameraAbility.getSupportedSizes(ImageFormat.JPEG)), mediaQuality);
        } else {
            return new Size(AttrHelper.vp2px(CONSTANT_600, context), AttrHelper.vp2px(CONSTANT_600, context));
        }
    }

    @Override
    public void stopVideoRecord(CameraFragmentResultListener listener) {
        if (isVideoRecording) {
            releaseVideoRecorder(); // 释放资源
            if (videoListener != null) {
                cameraKit.createCamera(mCurrentCameraId.toString(), cameraStateCallback, uiHandler);
                isVideoRecording = false;
                videoListener.onVideoRecordStopped(outputPath, listener);
            }
        }
    }

    @Override
    public void releaseCameraManager() {
        super.releaseCameraManager();
    }

    @Override
    public void initializeCameraManager(ConfigurationProvider configurationProvider, Context context) {
        super.initializeCameraManager(configurationProvider, context);
        cameraKit = CameraKit.getInstance(context);
        if (cameraKit == null) {
            return;
        }

        String[] cameraIds = cameraKit.getCameraIds(); // 获取当前设备的逻辑相机列表
        if (cameraIds.length <= 0) {
            Log.error(TAG, "cameraIds size is 0");
            return;
        }
        cameraIdString = cameraIds[0];
        numberOfCameras = cameraIds.length;
        int rotation = DisplayManager.getInstance().getDefaultDisplay(context).get().getRotation(); // 获取旋转常量值
        for (int index = 0; index < numberOfCameras; ++index) {
            CameraInfo cameraInfo = cameraKit.getCameraInfo(cameraIds[index]);
            int facingType = cameraInfo.getFacingType(); // 获取相机正面类型
            if (facingType == CameraInfo.FacingType.CAMERA_FACING_BACK) { // 后置摄像头
                faceBackCameraId = index;
                faceBackCameraOrientation = getOrientation(rotation);
            } else if (facingType == CameraInfo.FacingType.CAMERA_FACING_FRONT) { // 前置摄像头
                faceFrontCameraId = index;
                faceFrontCameraOrientation = getOrientation(rotation);
            }
        }
    }

    @Override
    public void initSurfaceComponent(DirectionalLayout directionalLayout, FractionAbility fraction,
                                         CameraOpenListener<Integer, SurfaceOps.Callback> listener) { // 初始化SurfaceView
        if (cameraIdString != null) {
            this.dependentLayout = directionalLayout;
            this.fractionAbility = fraction;
            mediaAction = Configuration.MEDIA_ACTION_PHOTO;
            surfaceProvider = new SurfaceProvider(context);
            surfaceProvider.pinToZTop(false);
            fractionAbility.getWindow().setTransparent(true); // 隐藏窗口
            prepareCameraOutputs(cameraIdString); // 初始化为拍照图片大小做准备
            takePictureInit(); // 设置图片大小
            surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallback());
            dependentLayout.addComponent(surfaceProvider);
        } else { // 当前相机不可用按钮禁止点击
            if (listener != null) {
                listener.onCameraOpenError();
            }
        }
    }

    @Override
    public CharSequence[] getVideoQualityOptions() {
        final List<CharSequence> videoQualities = new ArrayList<>();
        videoSizeList = new ArrayList<>();
        videoSizeList.clear();
        if (configurationProvider.getMinimumVideoDuration() > 0) {
            videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_AUTO,
                CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_AUTO,
                    getCurrentCameraId()), configurationProvider.getMinimumVideoDuration()));
        }

        RecorderProfile camcorderProfile = CameraHelper.getCamcorderProfile(
            Configuration.MEDIA_QUALITY_HIGH, getCurrentCameraId());
        double videoDuration = CameraHelper.calculateApproximateVideoDuration(
            camcorderProfile, configurationProvider.getVideoFileSize());
        videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_HIGH, camcorderProfile, videoDuration));
        videoSizeList.add(new Size(camcorderProfile.vFrameWidth, camcorderProfile.vFrameHeight)); // 添加视频分辨率

        camcorderProfile = CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_MEDIUM, getCurrentCameraId());
        videoDuration = CameraHelper.calculateApproximateVideoDuration(
            camcorderProfile, configurationProvider.getVideoFileSize());
        videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_MEDIUM,
            camcorderProfile, videoDuration));
        videoSizeList.add(new Size(camcorderProfile.vFrameWidth, camcorderProfile.vFrameHeight));

        camcorderProfile = CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_LOW, getCurrentCameraId());
        videoDuration = CameraHelper.calculateApproximateVideoDuration(
            camcorderProfile, configurationProvider.getVideoFileSize());
        videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_LOW,
            camcorderProfile, videoDuration));
        videoSizeList.add(new Size(camcorderProfile.vFrameWidth, camcorderProfile.vFrameHeight));

        final CharSequence[] array = new CharSequence[videoQualities.size()];
        videoQualities.toArray(array);

        return array;
    }

    @Override
    public void setMediaAction(int mediaAction) {
        this.mediaAction = mediaAction;
    }

    @Override
    public CharSequence[] getPhotoQualityOptions() {
        List<CharSequence> photoQualities = new ArrayList<>();
        photoSizeList = new ArrayList<>();
        photoSizeList.clear();
        photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_HIGHEST,
            getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_HIGHEST)));
        photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_HIGH,
            getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_HIGH)));
        photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_MEDIUM,
            getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_MEDIUM)));
        photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_LOWEST,
            getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_LOWEST)));

        photoSizeList.add(getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_HIGHEST));
        photoSizeList.add(getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_HIGH));
        photoSizeList.add(getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_MEDIUM));
        photoSizeList.add(getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_LOWEST));

        CharSequence[] array = new CharSequence[photoQualities.size()];
        photoQualities.toArray(array);
        return array;
    }

    /**
     * 拍照前准备图像帧数据的接收实现
     */
    private void takePictureInit() {
        // 创建ImageReceiver对象，注意create函数中宽度要大于高度；5为最大支持的图像数，请根据实际设置。
        imageReceiver = ImageReceiver.create(previewSize.getWidth(),
            previewSize.getHeight(), ImageFormat.JPEG, CONSTANT_5);
        ImageArrivalListener imageArrivalListener = new ImageArrivalListener();
        imageReceiver.setImageArrivalListener(imageArrivalListener);
    }

    private void releaseCamera() {
        if (cameraCallback != null) {
            // 关闭相机和释放资源
            cameraCallback.release();
            cameraCallback = null;
        }
        framePictureConfigBuilder = null; // 拍照配置模板置空
        frameRecordConfigBuilder = null; // 录像配置模板置空
        previewFrameConfig = null; // 预览配置模板置空
    }

    private void capture() {
        try {
            if (frameConfigBuilder == null) {
                // 获取拍照配置模板
                framePictureConfigBuilder = cameraCallback.getFrameConfigBuilder(FRAME_CONFIG_PICTURE);
                framePictureConfigBuilder.setFlashMode(Metadata.FlashMode.FLASH_ALWAYS_OPEN);
            } else {
                framePictureConfigBuilder = frameConfigBuilder;
            }

            framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface()); // 配置拍照Surface

            if (mCurrentCameraId.equals(faceFrontCameraId)) { // 判断是否为前置,是前置就旋转270
                framePictureConfigBuilder.setParameter(ParameterKey.IMAGE_MIRROR, true); // 设置镜像属性
                framePictureConfigBuilder.setImageRotation(ROTATE_270);
            } else { // 否则就是后置旋转90度
                framePictureConfigBuilder.setImageRotation(ROTATE_90); // 拍照时需要加上90角旋转,不然拍出来就图片会逆向旋转90度
            }
            framePictureConfig = framePictureConfigBuilder.build();
            cameraCallback.triggerSingleCapture(framePictureConfig); // 启动单帧捕获(拍照)
        } catch (IllegalArgumentException e) {
            Log.error(TAG, "capture IllegalArgumentException : " + e.getLocalizedMessage());
        } catch (IllegalStateException e) {
            Log.error(TAG, "capture IllegalStateException :" + e.getLocalizedMessage());
        }
    }

    // 重新显示预览层
    private void cameraPreview() {
        if (cameraCallback != null) {
            try {
                frameConfigBuilder = cameraCallback.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW); // 获取预览配置模板
                if (frameConfigBuilder != null) { // 配置预览Surface
                    frameConfigBuilder.addSurface(surface);
                    previewFrameConfig = frameConfigBuilder.build();
                    cameraCallback.triggerLoopingCapture(previewFrameConfig); // 启动循环帧捕获
                }
            } catch (IllegalArgumentException e) {
                Log.error(TAG, "cameraPreview ArgumentException " + e.getLocalizedMessage());
            } catch (IllegalStateException e) {
                Log.error(TAG, "cameraPreview StateException " + e.getLocalizedMessage());
            }
        }
    }

    // 录像配置模板
    private void cameraRecord() {
        try {
            if (cameraCallback != null) {
                frameRecordConfigBuilder = cameraCallback.getFrameConfigBuilder(FRAME_CONFIG_RECORD); // 获取录像配置模板
                frameRecordConfigBuilder.addSurface(surface); // 配置预览Surface
                Surface mRecorderSurface = videoRecorder.getVideoSurface(); // 配置录像的Surface
                frameRecordConfigBuilder.addSurface(mRecorderSurface);
                previewRecordConfig = frameRecordConfigBuilder.build();
                cameraCallback.triggerLoopingCapture(previewRecordConfig); // 启动循环帧捕获
            }
        } catch (IllegalArgumentException error) {
            Log.error(TAG, "cameraRecord ArgumentException " + error.getLocalizedMessage());
        } catch (IllegalStateException err) {
            Log.error(TAG, "cameraRecord StateException " + err.getLocalizedMessage());
        }
        if (videoRecorder != null) { // 录制视频
            videoRecorder.start();
            isVideoRecording = true;
            videoListener.onVideoRecordStarted(videoSize);
        }
    }

    /**
     * 根据不同分辨率显示对应宽高
     *
     * @param width 宽度
     * @param height 高度
     * @return Size 处理后的大小
     */
    private Size compare(int width, int height) {
        Size size = new Size(AttrHelper.vp2px(CONSTANT_600, context), AttrHelper.vp2px(CONSTANT_600, context)); // 默认宽高
        // 获取屏幕宽度和高度
        Optional<Display> display = DisplayManager.getInstance().getDefaultDisplay(context);
        Point pt = new Point();
        display.get().getSize(pt);
        double density = display.get().getAttributes().scalDensity;
        DecimalFormat df = new DecimalFormat("#.00");

        tempWidth = Double.parseDouble(df.format(width));
        tempHeight = Double.parseDouble(df.format(height));
        double wid1 = display.get().getAttributes().width;
        double hei1 = display.get().getAttributes().height;
        double wid = Double.parseDouble(df.format(wid1));
        double hei = Double.parseDouble(df.format(hei1));

        // 计算缩放比例
        double scaleX = wid / tempWidth;
        double scaleY = hei / tempHeight;
        double scale = 1.0;
        if (scaleX > scaleY && scaleX > 1) {
            scale = scaleX;
        }
        if (scaleY > scaleX && scaleY > 1) {
            scale = scaleY;
        }
        scale = Double.parseDouble(df.format(scale));
        scaleX = Double.parseDouble(df.format(scaleX));

        String odmProduct = System.getenv("ODM_PRODUCT");
        if (odmProduct.contains("ANA") || odmProduct.contains("TAS")) { // 说明是模拟器 因为模拟器状态默认是平躺,这里只需跟模拟器显示一样大小就OK
            size = setAnaSize(wid, hei);
        } else { // 真机
            if (mCurrentCameraId.equals(faceBackCameraId)) { // 后置摄像头
                size = setBackSize(size, wid, density, scaleX, scale);
            } else { // 前置
                size = setFrontSize(size, wid, hei, density, scale);
            }
        }
        return size;
    }

    private Size setFrontSize(Size size, double wid, double hei, double density, double scale) {
        double tempHeight1 = tempHeight;
        double scale1;
        if (mediaAction == Configuration.MEDIA_ACTION_VIDEO) { // 视频
            int mediaVideoQuality = configurationProvider.getMediaVideoQuality();
            if (mediaVideoQuality == Configuration.MEDIA_QUALITY_HIGH) {
                tempHeight1 = wid / (scale / CONSTANT_3_5);
                size.setWidth((int) (wid * CONSTANT_1_2));
                size.setHeight((int) tempHeight1);
            } else if (mediaVideoQuality == Configuration.MEDIA_QUALITY_MEDIUM) {
                tempHeight1 = wid / (scale / CONSTANT_6);
                size.setWidth((int) (wid * CONSTANT_1_35));
                size.setHeight((int) tempHeight1);
            } else {
                tempHeight1 = wid / (scale / CONSTANT_10);
                size.setWidth((int) (wid * CONSTANT_1_5));
                size.setHeight((int) tempHeight1);
            }
        } else {
            int mediaPhotoQuality = configurationProvider.getMediaPhotoQuality(); // 拍照
            if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_HIGHEST) {
                tempHeight1 = wid / (scale / CONSTANT_1_3);
                size.setWidth((int) wid);
                size.setHeight((int) tempHeight1);
            } else if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_HIGH) {
                tempHeight1 = wid / (scale / CONSTANT_1_35);
                size.setWidth((int) wid);
                size.setHeight((int) tempHeight1);
            } else if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_MEDIUM) {
                tempHeight1 = wid / (scale / CONSTANT_3_4);
                if (tempHeight1 > hei) {
                    tempHeight1 = tempHeight1 * CONSTANT_0_6;
                }
                size.setWidth((int) (wid * CONSTANT_1_1));
                size.setHeight((int) tempHeight1);
            } else {
                scale1 = scale - density;
                size.setWidth((int) wid);
                size.setHeight((int) (tempHeight1 * scale1));
            }
        }
        return size;
    }

    private Size setBackSize(Size size, double wid, double density, double scaleX, double scale) {
        double wid1 = wid;
        double tempHeight1 = tempHeight;
        double tempWidth1 = tempWidth;
        double scale1;
        if (mediaAction == Configuration.MEDIA_ACTION_VIDEO) { // 视频
            int mediaVideoQuality = configurationProvider.getMediaVideoQuality();
            if (mediaVideoQuality == Configuration.MEDIA_QUALITY_HIGH) {
                tempHeight1 = wid1 / (scale / CONSTANT_3);
                size.setWidth((int) (wid1 * CONSTANT_1_5));
                size.setHeight((int) tempHeight1);
            } else if (mediaVideoQuality == Configuration.MEDIA_QUALITY_MEDIUM) {
                tempHeight1 = wid1 / (scale / CONSTANT_5);
                size.setWidth((int) (wid1 * CONSTANT_1_6));
                size.setHeight((int) tempHeight1);
            } else {
                tempHeight1 = wid1 / (scale / CONSTANT_8);
                size.setWidth((int) (wid1 * CONSTANT_1_6));
                size.setHeight((int) tempHeight1);
            }
        } else {
            int mediaPhotoQuality = configurationProvider.getMediaPhotoQuality(); // 拍照
            if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_HIGHEST) {
                tempWidth1 = wid1 * CONSTANT_1_1;
                tempHeight1 = wid1 * CONSTANT_1_1;
                size.setWidth((int) tempWidth1);
                size.setHeight((int) tempHeight1);
            } else if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_HIGH) {
                tempWidth1 = wid1 * CONSTANT_1_1;
                tempHeight1 = wid1 * CONSTANT_1_1;
                size.setWidth((int) tempWidth1);
                size.setHeight((int) tempHeight1);
            } else if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_MEDIUM) {
                if (1 < scaleX) {
                    tempWidth1 = tempWidth1 / scaleX;
                } else {
                    tempWidth1 = tempWidth1 / (scaleX + CONSTANT_1_5);
                }
                size.setWidth((int) tempWidth1);
                size.setHeight((int) tempWidth1);
            } else {
                scale1 = (scale - density) / CONSTANT_1_35;
                tempHeight1 = tempHeight1 * scale1;
                size.setWidth((int) wid1);
                size.setHeight((int) tempHeight1);
            }
        }
        return size;
    }

    private Size setAnaSize(double wid, double hei) {
        Size size = new Size(AttrHelper.vp2px(CONSTANT_600, context), AttrHelper.vp2px(CONSTANT_600, context)); // 默认宽高
        double tempHei = 0.0;
        if (mediaAction == Configuration.MEDIA_ACTION_VIDEO) { // 录像
            int mediaVideoQuality = configurationProvider.getMediaVideoQuality();
            if (mediaVideoQuality == Configuration.MEDIA_QUALITY_HIGH) {
                tempHei = hei * CONSTANT_0_9;
            } else {
                tempHei = hei * CONSTANT_0_6;
            }
        } else {
            int mediaPhotoQuality = configurationProvider.getMediaPhotoQuality(); // 拍照
            if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_HIGHEST) {
                tempHei = hei * CONSTANT_0_9;
            } else if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_HIGH) {
                tempHei = hei * CONSTANT_0_9;
            } else if (mediaPhotoQuality == Configuration.MEDIA_QUALITY_MEDIUM) {
                tempHei = hei * CONSTANT_0_7;
            } else {
                tempHei = hei * CONSTANT_0_6;
            }
        }
        size.setWidth((int) wid);
        size.setHeight((int) tempHei);
        return size;
    }

    private void startPreview() { // 开始预览
        CameraAbility cameraAbility = cameraKit.getCameraAbility(cameraIdString);
        int cameraRotationOffset = DisplayManager.getInstance().getDefaultDisplay(context).get().getRotation();

        if (frameConfigBuilder == null && cameraCallback != null) {
            frameConfigBuilder = cameraCallback.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW);
        }
        setAutoFocus(cameraAbility, frameConfigBuilder);
        setFlashMode(configurationProvider.getFlashMode());

        if (configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_PHOTO
            || configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_UNSPECIFIED) {
            setAutoFocus(cameraAbility, frameConfigBuilder);
        }
        int rotation = DisplayManager.getInstance().getDefaultDisplay(context).get().getRotation();
        int degrees = 0;
        switch (rotation) {
            case 0:
                degrees = 0;
                break; // Natural orientation
            case 1:
                degrees = ROTATE_90;
                break; // Landscape left
            case CONSTANT_2:
                degrees = ROTATE_180;
                break; // Upside down
            case CONSTANT_3:
                degrees = ROTATE_270;
                break; // Landscape right
            default:
                break;
        }
        CameraInfo cameraInfo = cameraKit.getCameraInfo(cameraIdString);
        int facingType = cameraInfo.getFacingType(); // 获取相机正面类型
        if (facingType == CameraInfo.FacingType.CAMERA_FACING_FRONT) {
            displayRotation = (cameraRotationOffset + degrees) % ROTATE_360;
            displayRotation = (ROTATE_360 - displayRotation) % ROTATE_360; // compensate
        } else {
            displayRotation = (cameraRotationOffset - degrees + ROTATE_360) % ROTATE_360;
        }
    }

    /**
     * 单帧捕获生成图像回调Listener
     *
     * @since 2021-05-24
     */
    private class ImageArrivalListener implements ImageReceiver.IImageArrivalListener, ImageSaver.ImageSaverCallback {
        @Override
        public void onImageArrival(ImageReceiver image) {
            try {
                Image readNextImage = image.readNextImage();
                File myFile = new File(outputPath.getCanonicalPath()); // 创建图片文件
                backgroundHandler.postTask(new ImageSaver(readNextImage, myFile, this)); // 执行读写线程任务生成图片
            } catch (IOException error) {
                Log.error(TAG, " onImageArrival IOException error " + error.getLocalizedMessage());
            }
        }

        @Override
        public void onSuccessFinish(byte[] bytes) {
            if (photoListener != null) {
                uiHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        photoListener.onPhotoTaken(outputPath, callback);
                        callback = null;
                    }
                });
            }
        }

        @Override
        public void onError() {
            uiHandler.postTask(new Runnable() {
                @Override
                public void run() {
                    photoListener.onPhotoTakeError();
                }
            });
        }
    }

    /**
     * 帧捕获状态回调
     *
     * @since 2021-05-24
     */
    private static class FrameStateCallBack extends FrameStateCallback {
        @Override
        public void onFrameStarted(Camera camera, FrameConfig frameConfig, long frameNumber, long timestamp) {
        }

        @Override
        public void onFrameProgressed(Camera camera, FrameConfig frameConfig, FrameResult frameResult) {
        }

        @Override
        public void onFrameFinished(Camera camera, FrameConfig frameConfig, FrameResult frameResult) {
        }

        @Override
        public void onFrameError(Camera camera, FrameConfig frameConfig, int errorCode, FrameResult frameResult) {
        }

        @Override
        public void onCaptureTriggerStarted(Camera camera, int captureTriggerId, long firstFrameNumber) {
        }

        @Override
        public void onCaptureTriggerFinished(Camera camera, int captureTriggerId, long lastFrameNumber) {
        }

        @Override
        public void onCaptureTriggerInterrupted(Camera camera, int captureTriggerId) {
        }
    }

    /**
     * 相机状态回调
     *
     * @since 2021-04-14
     */
    private class CameraStateCallbackImpl extends CameraStateCallback {
        private boolean isRecord;

        /**
         * 构造参数
         *
         * @param isRecord 是否为录像操作
         */
        CameraStateCallbackImpl(boolean isRecord) {
            this.isRecord = isRecord;
        }

        @Override
        public void onCreated(Camera camera) {
            // 创建相机设备
            cameraCallback = camera;
            CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
            if (cameraConfigBuilder == null) {
                Log.error(TAG, "onCreated cameraConfigBuilder is null");
                return;
            }
            cameraConfigBuilder.addSurface(surface); // 配置预览的Surface
            if (isRecord) {
                cameraConfigBuilder.addSurface(videoRecorder.getVideoSurface());
            } else {
                // 配置拍照的Surface
                cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
            }
            FrameStateCallBack frameStateCallbackImpl = new FrameStateCallBack();
            cameraConfigBuilder.setFrameStateCallback(frameStateCallbackImpl, uiHandler); // 配置帧结果的回调
            try {
                // 相机设备配置
                camera.configure(cameraConfigBuilder.build());
            } catch (IllegalArgumentException error) {
                Log.error(TAG, "Argument Exception " + error.getLocalizedMessage());
            } catch (IllegalStateException err) {
                Log.error(TAG, "State Exception " + err.getLocalizedMessage());
            }
        }

        @Override
        public void onConfigured(Camera camera) {
            if (isRecord) {
                // 获取录像配置模板
                cameraRecord();
            } else {
                // 获取预览配置模板
                cameraPreview();
            }
        }
    }

    /**
     * Surface创建回调
     *
     * @since 2021-04-10
     */
    private class SurfaceCallback implements SurfaceOps.Callback {
        @Override
        public void surfaceCreated(SurfaceOps surfaceOps) {
            surface = surfaceOps.getSurface();
            cameraStateCallback = new CameraStateCallbackImpl(false); // 相机创建和相机运行时的回调
            cameraStateCallback2 = new CameraStateCallbackImpl(true);
            cameraKit.createCamera(cameraIdString, cameraStateCallback, uiHandler); // 创建相机
            setSurfaceSize();
        }

        @Override
        public void surfaceChanged(SurfaceOps surfaceOps, int ii, int i1, int i2) {
        }

        @Override
        public void surfaceDestroyed(SurfaceOps surfaceOps) {
        }
    }

    /**
     * 打开相机预览页面回调
     *
     * @since 2021-04-10
     */
    private static class OpenCameraSurfaceCallback implements SurfaceOps.Callback {
        @Override
        public void surfaceCreated(SurfaceOps surfaceOps) {
        }

        @Override
        public void surfaceChanged(SurfaceOps surfaceOps, int ii, int i1, int i2) {
        }

        @Override
        public void surfaceDestroyed(SurfaceOps surfaceOps) {
        }
    }
}
